#pragma once

#include <cstdio>
#include "Protocol.hpp"
#include "TcpSocket.hpp"
#include "UnCopy.hpp"
#include "ThreadPool.hpp"
#include "TcpListenSocket.hpp"
#include "CalculateService.hpp"

using task_t = std::function<void()>;

class DlpServer : public UnCopy
{
public:
    DlpServer(uint16_t port) : _port(port), _tcpListenSocket(port)
    {
        ThreadPool<task_t>::getInstance()->start();
        _factory = std::make_unique<DLP::ReqRespFactory>();
    }

    void start()
    {
        while(true)
        {
            std::string peerIp;
            uint16_t peerPort;
            TcpConnectSocket TcpConnectSocket = _tcpListenSocket.accept(&peerIp, &peerPort);

            task_t task = std::bind(&DlpServer::processRequest, this, TcpConnectSocket, peerIp, peerPort);
            ThreadPool<task_t>::getInstance()->submit(task);
        }
    }

    void processRequest(TcpConnectSocket TcpConnectSocket, std::string peerIp, uint16_t peerPort)
    {
        std::string dlpRequestStream = "";
        std::string dlpRequestBuffer;
        while(true)
        {
            ssize_t n = TcpConnectSocket.readStream(dlpRequestBuffer);
            if(n > 0)
            {
                std::string content;
                dlpRequestStream += dlpRequestBuffer;
                while(DLP::dlpDecoding(dlpRequestStream, content))
                {
                    std::shared_ptr<DLP::Request> req = _factory->makeRequest();
                    req->deserialize(content);
                    int code = -1;
                    int result = _calculateService.doCalculate(req->getX(), req->getY(), req->getOp(), code);

                    std::shared_ptr<DLP::Response> resp = _factory->makeResponse(result, code);
                    std::string dlpResponse = DLP::dlpEncoding(resp->serialize());

                    printf("[%s:%s] req=%s resp=%s\n", peerIp.c_str(), std::to_string(peerPort).c_str(), content.c_str(), resp->serialize().c_str());
                    TcpConnectSocket.writeStream(dlpResponse);
                }
            }
            else if(n == 0)
            {
                log.message(INFO, "socket read to the end, client is quit...");
                break;
            }
            else
            {
                log.message(ERROR, "socket read error, %d: %s", errno, strerror(errno));
            }
        }
    }

    ~DlpServer()
    {}
private:
    uint16_t _port;
    TcpListenSocket _tcpListenSocket;
    std::unique_ptr<DLP::ReqRespFactory> _factory;
    CalculateService _calculateService;
};